/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package filecopier.copy;

import java.io.File;
import java.io.IOException;
import java.io.FilenameFilter;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Formatter;
import filecopier.Singleton;
import filecopier.ActionCancelException;
import filecopier.script.Script;
import filecopier.script.CopyScript;
import filecopier.script.ScriptEntry;
import filecopier.options.Options;
import custom.xml.DOMHelperException;
import custom.util.FileCopy;
import custom.util.FileCopyHelper;
import custom.io.FileNameFilterWrapper;

/**
 *
 * @author david
 */
public abstract class AbstractCopyWorker {

    protected Script fromScript = null;
    protected Script toScript = null;

    public AbstractCopyWorker(final Script fromScript, final Script toScript) {
        if (fromScript == null) {
            throw new IllegalArgumentException("No From Script specified");
        }
        if (toScript == null) {
            throw new IllegalArgumentException("No To Script specified");
        }
        this.fromScript = fromScript;
        this.toScript = toScript;
    }

    protected List<File> getFiles(Script script) {
        if (script == null) {
            throw new IllegalArgumentException("getFiles was passed null for its Script object.");
        }

        List<File> files = new ArrayList<File>();

        List<ScriptEntry> entries = script.getScriptEntries();
        for (ScriptEntry se : entries) {
            File pathEntry = se.getPath();
            if (se.getPathType() == ScriptEntry.PathType.Normal) {
                files.add(pathEntry);
            } else {
                try {
                    Script innerScript = new CopyScript(pathEntry);
                    List<File> innerFiles = getFiles(innerScript);
                    files.addAll(innerFiles);
                } catch (DOMHelperException e) {
                    // Log only to the log file.
                    Singleton.getInstance().getFileLogger().error("\"" + pathEntry + "\", a script referenced in \"" +
                            script.getScriptName() + "\", is not a valid script file.", e);
                } catch (IOException e) {
                    Singleton.getInstance().getFileLogger().error("\"" + pathEntry + "\", a script referenced in \"" +
                            script.getScriptName() + "\", is not a valid script file.", e);
                }
            }
        }

        return files;
    }

    protected FileCopy getFileCopy(final FileCopyHelper fileCopyHelper) {
        Options options = Singleton.getInstance().getOptions();

        // Get the dir flatten filter and dir merge filter.
        FilenameFilter dirFlattenFilter = options.getRegExFilenameFilter(Options.SrchFilter.DirFlattenFilter);

        FilenameFilter dirMergeFilter = options.getRegExFilenameFilter(Options.SrchFilter.DirMergeFilter);

        FileCopy fileCopy = new FileCopy(fileCopyHelper, getFilenameFilter(options),
                dirFlattenFilter, dirMergeFilter, true, options.getMaxDirFlattenLevel(),
                options.getMaxDirMergeLevel(), options.getMaxDirCopyLevel());
        return fileCopy;
    }

    protected FilenameFilter getFilenameFilter(final Options options) {
        FilenameFilter filenameFilter = null;
        FilenameFilter[] filters = new FilenameFilter[Options.DIR_COPY_FILTER + 1];

        filters[Options.FILE_COPY_FILTER] = options.getRegExFilenameFilter(Options.SrchFilter.FileCopyFilter);
        filters[Options.DIR_COPY_FILTER] = options.getRegExFilenameFilter(Options.SrchFilter.DirCopyFilter);

        filenameFilter = new FileNameFilterWrapper(filters);

        return filenameFilter;
    }

    protected File buildDefaultRestoreScriptFile(final Script fromScript, final Script toScript) {
        File restoreScriptFile = null;

        if ((fromScript != null) && (toScript != null)) {
            // Get the from script and to script names without the extension
            String fromScriptName = fromScript.getScriptFile().getName();
            int idx = fromScriptName.lastIndexOf('.');
            if (idx > -1) {
                fromScriptName = fromScriptName.substring(0, idx);
            }

            String toScriptName = toScript.getScriptFile().getName();
            idx = toScriptName.lastIndexOf('.');
            if (idx > -1) {
                toScriptName = toScriptName.substring(0, idx);
            }

            // Get the current time
            Calendar currentTime = Calendar.getInstance();

            // Build the restore script name based on the from script name, to script name, and the current time.
            Formatter fmt = new Formatter();
            fmt.format(".%tY%<tm%<td.%<tI%<tM%<tS%<tL", currentTime);
            String restoreScriptName = fromScriptName + "." + toScriptName + fmt.toString();

            // Put the full path of the restore script in the instance variable for it.
            //String restoreScriptPath = System.getProperty("filecopier.scripts.restore") +
            //System.getProperty("file.separator") + restoreScriptName + ".rsc";
            
            String restoreScriptPath =
                Singleton.getInstance().getOptions().getRestorePathWrapper().getRestorePath().getPathArray()[0] +
                System.getProperty("file.separator") + restoreScriptName + ".rsc";
            restoreScriptFile = new File(restoreScriptPath);
            // Ensure that the restore script name is unique.
            restoreScriptFile = ensureRestoreScriptUniqueness(restoreScriptFile);
        }
        return restoreScriptFile;
    }

    protected int countFiles(final List<File> fromFiles, final List<File> toFiles, final Options options) {
        int ct = 0;

        // Get the number of from files. Some of the from files may be directories, so we need to go into them and count
        // the files in them.
        File[] fromFileArray = new File[fromFiles.size()];
        fromFileArray = fromFiles.toArray(fromFileArray);

        for (File fromFile : fromFileArray) {
            // We need to get the copy file name filters and the leve of directories to copy and pass them to
            // countFromFiles. These values are needed to determine the number of files we are supposed to copy.
            ct += countFiles(fromFile, 0, getFilenameFilter(options), options.getMaxDirCopyLevel());
        }

        // Multiply the number of from files by the number of to files. To files should all be directories.
        ct *= toFiles.size();

        return ct;
    }

    private int countFiles(final File fromFile, int curDirLevel, final FilenameFilter copyFilenameFilter, int copyDirLevel) {
        int ct = 0;

        if (fromFile != null) {
            if (!fromFile.isDirectory()) {
                ct++;
            } else {
                curDirLevel++;
                // See if we are supposed to copy this directory.
                if (toCopy(fromFile, curDirLevel, copyDirLevel, copyFilenameFilter)) {
                    File[] innerFiles = fromFile.listFiles(copyFilenameFilter);
                    for (File f : innerFiles) {
                        ct += countFiles(f, curDirLevel, copyFilenameFilter, copyDirLevel);
                    }
                }
            }
        }
        return ct;
    }

    private boolean toCopy(final File source, int curDirLevel, int copyDirLevel, final FilenameFilter copyFilenameFilter) {
        boolean copy = copyFilenameFilter.accept(source.getParentFile(), source.getName());

        if (copy) {
            copy = (copyDirLevel == custom.util.FileCopy.COPY_ALL_DIRS || copyDirLevel >= curDirLevel);
        }
        return copy;
    }

    /** This method ensures the uniqueness of the restore script's name. */
    private File ensureRestoreScriptUniqueness(File restoreScriptFile) {
        String initialName = restoreScriptFile.getName();

        for (int count = 1; restoreScriptFile.exists(); ++count) {
            // This filename already exists. We must come up with a new one.
            String restoreScriptName = initialName + "[" + count + "]";
            String restoreScriptPath = System.getProperty("filecopier.scripts.restore") +
            System.getProperty("file.separator") + restoreScriptName;
            restoreScriptFile = new File(restoreScriptPath);
        }

        return restoreScriptFile;
    }

    public abstract void copyFiles() throws ActionCancelException;
}
